﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Reflection;

namespace ObjectEditor.UserControls
{
    public sealed class PropertyEditorMultiObjectViewModel : ViewModel, IDisposable
    {
        public static object CopyFrom(object obj, object otherObject,IEnumerable<string> propertyNames)
        {
            PropertyInfo[] srcFields = otherObject.GetType().GetProperties(
                BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty);

            PropertyInfo[] destFields = obj.GetType().GetProperties(
                BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty);

            foreach (var property in srcFields.Where( x => propertyNames.Contains(x.Name)))
            {
                var dest = destFields.FirstOrDefault(x => x.Name == property.Name);
                if (dest != null && property.CanWrite)
                    dest.SetValue(obj, property.GetValue(otherObject, null), null);
            }

            return obj;
        }

        private IEnumerable<INotifyPropertyChanged> _objects;
        private INotifyPropertyChanged _object;
        private Dictionary<string, object> _changedProperties = new Dictionary<string, object>();
        private DelegateCommand _copyPropertiesCommand;

        public PropertyEditorMultiObjectViewModel(IEnumerable<INotifyPropertyChanged> objects)
        {
            _objects = objects;
            if (objects != null && objects.Count() > 0)
            {
                _object = objects.ElementAt(0);
                _object.PropertyChanged += _object_PropertyChanged;
            }
        }

        void _object_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            _changedProperties[e.PropertyName] = sender.GetType().GetProperty(e.PropertyName).GetValue(sender);
            CopyPropertiesCommand.RaiseCanExecuteChanged();
        }

        public DelegateCommand CopyPropertiesCommand
        {
            get
            {
                if (_copyPropertiesCommand == null)
                {
                    _copyPropertiesCommand = new DelegateCommand(
                        x =>
                        {
                            List<string> propertiesChanged = new List<string>();

                            var result = System.Windows.MessageBox.Show(
                                "Apply property changes to all objects?" + Environment.NewLine + _objects.Count() + " selected"
                                , "Apply changes?", System.Windows.MessageBoxButton.YesNo);
                            if (result == System.Windows.MessageBoxResult.Yes)
                            {
                                foreach (var kvp in _changedProperties)
                                {
                                    propertiesChanged.Add(kvp.Key);
                                }

                                foreach (var obj in _objects.Where(o => o != _object))
                                {
                                    CopyFrom(obj, _object, propertiesChanged);
                                }
                            }
                        },
                        y =>
                        {
                            return _changedProperties.Count > 0 && _object != null && _objects.Count() > 1;
                        });
                }
                return _copyPropertiesCommand;
            }
        }

        public INotifyPropertyChanged SourceObject
        {
            get
            {
                return _object;
            }
            set
            {
                _object = value;
                OnPropertyChanged("SourceObject");
            }
        }

        public void Dispose()
        {
            if (_object != null)
            {
                _object.PropertyChanged -= _object_PropertyChanged;
            }
        }
    }
}
